#include <interface.h>
#include <iostream>

DECLARE_DRAWABLE_BEGIN(Coordinate, "Getting started/Coordinate Systems", 1,8,1)
  virtual void init() override {
    // build and compile our shader program
    // ------------------------------------
    shader_.loadPath("texture.vs", "texture.fs");

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
      -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
      0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
      0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
      0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
      -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
      -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

      -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
      0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
      0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
      0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
      -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
      -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

      -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
      -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
      -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
      -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
      -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
      -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

      0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
      0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
      0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
      0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
      0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
      0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

      -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
      0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
      0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
      0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
      -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
      -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

      -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
      0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
      0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
      0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
      -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
      -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };

    glGenVertexArrays(1, &VAO_);
    glGenBuffers(1, &VBO_);

    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO_);

    glBindBuffer(GL_ARRAY_BUFFER, VBO_);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // texture coord attribute
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    texture1_.loadPath("container.jpg");
    texture2_.loadPath("awesomeface.png");

    shader_.use();
    glUniform1i(glGetUniformLocation(shader_.id, "texture1"), 0);
    shader_.setInt("texture2", 1);

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    glBindVertexArray(0);

    /////////////////////////////
    model_ = glm::mat4();
    view_ = glm::mat4();
    projection_ = glm::mat4();
    rotateSpeed_ = 30.f;

    model_ = glm::rotate(model_, glm::radians(-55.f), glm::vec3(1.f, 0.f, 0.f));
    view_ = glm::translate(view_, glm::vec3(0.f, 0.f, -3.f));
  }
  virtual void uninit() override {
    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO_);
    glDeleteBuffers(1, &VBO_);
    texture1_.free();
    texture2_.free();
    shader_.free();
  }
  virtual void update(float elapsedTime) override {
    float radians = glm::min(elapsedTime * rotateSpeed_, 360.f);
    radians = glm::max(radians, 0.f);
    
    glGetIntegerv(GL_VIEWPORT, viewport_.value);
    
    model_ = glm::rotate(model_, glm::radians(radians), glm::vec3(0.5f, 0.1f, 0.f));
    projection_ = glm::perspective(glm::radians(45.f), viewport_.aspect() , 0.1f, 100.f);

    shader_.setMat4("model", model_);
    shader_.setMat4("view", view_);
    shader_.setMat4("projection", projection_);
  }
  virtual void render() override {
    // render the triangle
    // bind textures on corresponding texture units
    texture1_.use(GL_TEXTURE0);
    texture2_.use(GL_TEXTURE1);

    shader_.use();
    glBindVertexArray(VAO_);
    glDrawArrays(GL_TRIANGLES, 0, 36);
  }
 protected:
  GLuint shaderProgram_;
  GLuint VAO_, VBO_;
  Shader shader_;
  Texture texture1_, texture2_;
  float rotateSpeed_;
  glm::mat4 model_, view_, projection_;
  union {
    struct {
      GLint x, y, w, h;
    };
    GLint value[4];
    float aspect() { return (w / h); }
  } viewport_;
DECLARE_DRAWABLE_END(Coordinate)